# coding=utf-8 
'''
Created on 2018年2月28日

@author: heguofeng

todo:
    add ack process of  shell 
    shell dir 不支持中文
    ftp 大文件
'''
import unittest
from pp_network import PPStation,PPMessage,PP_APPID,PPApp,\
    PPNetApp, FakeAppNet
import struct
import time
import os
import threading
import subprocess
import yaml
import sys

import logging
from pp_file import Filer, FileBlock
from pp_link import NAT_TYPE,set_debug

SHELL_TAG={
        "command": 0x30, #SHELL_IN = 0x30
        "output": 0x31, #SHELL_OUT = 0x31
        "error": 0x32 ,# SHELL_ERROR = 0x32
        "command_id": 0x33,#SHELL_IN_ID = 0x33
        "output_len":0x34,#SHELL_OUT_LEN = 0x34
        "start": 0x35,#SHELL_START = 0x35
        "end": 0x36,#SHELL_END  = 0x36
        "work_dir":0x37,
           }

class ShellMessage(PPMessage):
    '''
    src_id   dst_id   app_id  sequence applen  appdata
    4byte    4byte    2byte   4byte   2byte   applen
    
    appid:     app        appdata


    
    0016    shell          input len command output len resultstring error len errorstring
    

    
    '''
                
    def __init__(self,**kwargs):
        
        super().__init__(**kwargs)
        self.dict_data["app_id"] = PP_APPID["Shell"]

   
    def load(self,bindata):

        self.dict_data["parameters"]={}
        start_pos = 0
        while start_pos < len(bindata):
            pos,result = self.load_TLV(bindata[start_pos:])
            self.dict_data["parameters"][result[0]]=result[2]
            start_pos += pos
        return
    
    def dump(self):
        data=b""
        for tag in self.dict_data["parameters"]:
            data += self.dump_TLV((tag,len(self.dict_data["parameters"][tag]),self.dict_data["parameters"][tag]))  
        return data

def get_os_cwd():
    if sys.platform.startswith("win") :
        return "cd"
    else:
        return "pwd"
    
def get_fullpath(work_dir,filename):    
    if sys.platform.startswith("win"):
        fullpath_filename =filename  if filename.find(":")>0 else os.path.join(work_dir,filename)
    else:
        fullpath_filename =filename  if filename.startswith("/") else os.path.join(work_dir,filename)
#     logging.debug("fullpath:%s"%fullpath_filename)
    return fullpath_filename

class ShellServer(object):
    def __init__(self,connection, work_dir="."):
        self.work_dir  =  work_dir
        self.connection = connection
        self.command = None
        self.command_id = 0
        self.stage = 0  #0 init  1 waiting  2 finished
        self.return_code = 0
        self.output = None
        self.output_size = 0
        if self.work_dir==".":
            self.work_dir = self._run(get_os_cwd())[1].decode().splitlines()[-1]
#             print(self.work_dir)


    def quit(self):
        self.stage = 2
        
    def command_process(self,parameters):
        if SHELL_TAG["command"] in parameters:
            self.run_command(command = parameters[SHELL_TAG["command"]].decode(),
                                command_id = struct.unpack("I",parameters[SHELL_TAG["command_id"]])[0],
                                start =  struct.unpack("I",parameters[SHELL_TAG["start"]])[0],
                                end =  struct.unpack("I",parameters[SHELL_TAG["end"]])[0])        
    
    def run_command(self,command_id,command,start=0,end=0):
        logging.info("\nrun command (%d)%s cwd=%s start  %d end=%d .my id=%d "%(command_id,
                                                                        command,self.work_dir,start,end,self.command_id))
        if command_id == self.command_id:
            self.send_output(self.command_id, start, end)
        else:
            self.command = command
            self.command_id = command_id
            if command.startswith("cd") or (len(command)>1 and command[1]==":"):
                return_code,path = self._run(command +"&&"+get_os_cwd(),self.work_dir)
                if return_code:
                    logging.warning(path)
                    self.info(self.command_id,path,error_code= return_code)
                else:
                    self.work_dir =path.decode().splitlines()[-1]
                    logging.debug("%d working dir:%s"%(self.connection[0].node_id,self.work_dir))
                    self.info(self.command_id,self.work_dir.encode(),error_code= None)
                self.return_code = 0
                self.output =  b""
            else:
                self.return_code,self.output = self._run(command,self.work_dir)
            self.output +=  b"\n$"+self.work_dir.encode() +b">" 
            self.output_size = len(self.output)
            self.stage = 1
            self.send_output(self.command_id, start=0, end=self.output_size)
        
    def _run(self,command,cwd="."):
        try:
            output = subprocess.check_output(command ,cwd = cwd, shell=True)
            if sys.platform.startswith("win"):
                return 0,output.decode("gbk").encode()
            else:
                return 0,output.decode().encode()
        except Exception as cmd_err:
            return 1,cmd_err.__str__().encode()

    def send_output(self,command_id,start,end=0):
        '''
        start = output_size  finished
        end_pos = 0  ==>output_size
        '''
        if not command_id == self.command_id:
            return 0

        if self.stage != 1:
            return self.output_size
        
        if start == self.output_size:
            logging.debug("\n%s send compelete! output_size %d . "%(self.command,self.output_size))
            self.quit()
            return self.output_size

        real_end = end if end else self.output_size
            
        logging.debug("send output with start = %d  end= %d"%(start,real_end))
                
        for i in range(0,int((real_end-start+1023)/1024)):
            print(">",end="")
            tempstart = i*1024+start
            tempend = (i+1)*1024+start if (i+1)*1024+start<real_end else real_end

            tempoutput =  self.output[tempstart:tempend]
            dictdata = {"parameters":{ SHELL_TAG["output"]:tempoutput,
                                       SHELL_TAG["command_id"]:struct.pack("I", self.command_id),
                                       SHELL_TAG["output_len"]:struct.pack("I", self.output_size),
                                       SHELL_TAG["start"]:struct.pack("I", tempstart),
                                       SHELL_TAG["end"]:struct.pack("I", tempend),
                                       }}
            if tempstart==0:
                dictdata["parameters"][SHELL_TAG["error"]]=struct.pack("I", self.return_code)
            shell_msg = ShellMessage(dictdata=dictdata)
            self.connection[0].send_msg(self.connection[1], shell_msg,need_ack=False)    
        return
    
    def info(self,command_id,info,error_code=None):
        real_info = info+b"\n$"+self.work_dir.encode()+b">"
        dictdata = {"parameters":{ SHELL_TAG["output"]:real_info,
                                   SHELL_TAG["command_id"]:struct.pack("I", command_id),
                                   SHELL_TAG["output_len"]:struct.pack("I", len(real_info)),
                                   SHELL_TAG["start"]:struct.pack("I", 0),
                                   SHELL_TAG["end"]:struct.pack("I", len(real_info)),
                                   SHELL_TAG["work_dir"]:self.work_dir.encode(),
                                   }}
        if not error_code==None:
            dictdata["parameters"][SHELL_TAG["error"]]=struct.pack("I", error_code)

        shell_msg = ShellMessage(dictdata=dictdata)
        self.connection[0].send_msg(self.connection[1], shell_msg,need_ack=True)    
        
    
class ShellClient(object):
    def __init__(self,connection):
        self.connection = connection
        self.command_id = 0
        self.history = {}
        self.server_work_dir = ""
        self.command = ""
        self.set_stage(stage = 0)
#         self.check(status=False)


    def set_stage(self,stage):
        '''
        stage 0  start  to  execute 
              1  waiting result
              2  finish
        '''
        self.stage = stage
        self.write_pos = 0
        self.buffer = {}
        self.output = b""
        self.retry_count = 0
        self.waiting = (0,0)
        self.receiving = False

        self.checking = True if self.stage==1 else  False 
        if self.stage==1:
            self._check()
        
        

                
    def send_command(self,command):
        self.command_id += 1
        self.command = command
        self.history[self.command_id] = command
        self.set_stage(stage=1)


        
    def get_output(self,start,end=0):
        logging.debug("get (%d)%s output with start = %d  end= %d"%(self.command_id,self.command,start,end))

        shell_msg = ShellMessage(dictdata={
                                       "parameters":{
                                                    SHELL_TAG["command"]:self.command.encode(),
                                                    SHELL_TAG["command_id"]:struct.pack("I",self.command_id),
                                                    SHELL_TAG["start"]:struct.pack("I",start),
                                                    SHELL_TAG["end"]:struct.pack("I",end)}
                                       })
        self.connection[0].send_msg(self.connection[1], shell_msg,need_ack=True)        
        pass    
    
    def receive_process(self,parameters):
        logging.debug("receive shell message %s"%(parameters))

        if SHELL_TAG["error"] in parameters:
            logging.debug("ShellCode:%d shell_in_id=%d"%(struct.unpack("I", parameters[SHELL_TAG["error"]])[0],
                            struct.unpack("I",parameters[SHELL_TAG["command_id"]])[0]))
        if SHELL_TAG["work_dir"] in parameters:
            self.server_work_dir = parameters[SHELL_TAG["work_dir"]].decode()
            logging.debug("server dir = %s"%self.server_work_dir)
        if SHELL_TAG["output"] in parameters:
            self.receive(command_id =struct.unpack("I",parameters[SHELL_TAG["command_id"]])[0],
                       output_size =struct.unpack("I",parameters[SHELL_TAG["output_len"]])[0], 
                       start = struct.unpack("I",parameters[SHELL_TAG["start"]])[0],
                       end = struct.unpack("I",parameters[SHELL_TAG["end"]])[0],
                       output_data = parameters[SHELL_TAG["output"]] )
    
    def receive(self,command_id,output_size,start,end,output_data):

        if output_size==0:
            return 0
        
        logging.debug("currentpos %d,output size %d start %d end %d"%(self.write_pos,output_size,start,end,))
        if command_id:
            if not command_id == self.command_id:
                return
        self.receiving = True
        if start>=self.write_pos:
            self.buffer[start]=(end,output_data)
            self.checking = True
        self._write()
        if self.write_pos >= output_size:
            print(self.output.decode(),end="")
            logging.info("%s  compelete! size %d . (%d)"%(self.command_id,output_size,
                                                                 self.write_pos))
            self.set_stage(stage = 2)
            return output_size
        else:
            return 0
            
    def _write(self):
        if self.write_pos in self.buffer:
            pos = self.write_pos
            self.output += self.buffer[pos][1]

            self.write_pos += self.buffer[pos][0]-pos
            del self.buffer[pos]
            self._write()
        else:
            if len(self.buffer):
                startlist = list(self.buffer.keys())
                startlist.sort()
                minstart = startlist[0]
                if not self.waiting == (self.write_pos,minstart) and self.stage == 1:
                    self.get_output(minstart)
                    self.waiting = (self.write_pos,minstart)
            pass
        

    def _check(self):
        if not self.stage ==1:
            return
        
        if self.receiving:
            self.receiving = False
            self.retry_count = 0
        else: 
            self.retry_count += 1
            if self.retry_count <10 :
                self.get_output(self.write_pos,0)
            else:
                logging.warning("network error:can't connect server!")                
                print("network error:can't connect server!")
                self.set_stage(stage = 2)
            
        if self.stage ==1:
            threading.Timer(3,self._check).start()
        pass
   
class Sheller(PPNetApp):
    def __init__(self,station):
        super().__init__(station,PP_APPID["Shell"])
        self.peer_id = 0
        self.shell_client = None
        self.shell_server = None
        self.start()
        
    def connect(self,peer_id):
        self.peer_id = peer_id
#         self.station.set_app_process(PP_APPID["Shell"],self.shell_process)
        self.shell_server = ShellServer((self.station,self.peer_id))
        self.shell_client = ShellClient((self.station,self.peer_id))
        pass
    
    def disconnect(self):
        self.peer_id = 0 
        self.shell_client = None
        self.shell_server = None
#         self.station.set_app_process(PP_APPID["Shell"],None)
        
    
    def shell_command(self,command):
        if self.peer_id:
            self.shell_client.send_command(command)
            
    def process(self,ppmsg,addr):
        if not self.peer_id:
            logging.warning("%d no auth, should connect "%self.station.node_id)
            return
        fs_message = ShellMessage(bindata=ppmsg.get("app_data"))
        parameters = fs_message.get("parameters")

        if SHELL_TAG["command"] in parameters:
            self.shell_server.command_process(parameters)

        if SHELL_TAG["error"] in parameters or SHELL_TAG["output"] in parameters:
            self.shell_client.receive_process(parameters)

        return   
    
class Auther(PPNetApp):
    
    class AuthMessage(PPApp.AppMessage):
        '''
        appid  app     appdata
        001   Auth     NodeId,Application,cmd, UserName,Secret,
                          4       2        1   tlv      tlv  
        
        command:
        login     0x01
        logout    0x02
        ack_ok    0x03
    
        parameters:
        nodeid    TLV    0x15
        appid     2byte
        username  TLV    0x10
        secret    TLV    0x11
        info      TLV    0x12
        code      TLV    0x13
        auth_app  TLV    0x14
        '''
        def __init__(self,**kwargs):

            tags_id = {
              "login": 0x01,"logout": 0x02,
              "username": 0x10, "secret": 0x11,"auth_app":0x14,"node_id":0x15,       
              }
            tags_string = {
              1:"login",2:"logout",
              0x10:"username",0x11: "secret", 0x14:"auth_app" ,0x15:"node_id",
               }
            parameter_type = {0x10:"str",0x11:"str",0x14:"H",0x15:"I"}
            super().__init__(app_id=PP_APPID["Auth"],
                             tags_id=tags_id,
                             tags_string=tags_string,
                             parameter_type=parameter_type,
                             **kwargs)
#         def load(self,bindata):
#             node_id,auth_app,command = struct.unpack("IHB",bindata[0:7])
#             self.dict_data["parameters"]={}
#             start_pos = 7
#             while start_pos < len(bindata):
#                 pos, result = self.load_TLV(bindata[start_pos:])
#                 self.dict_data["parameters"][self.tags_string[result[0]]] = result[2].decode()
#                 start_pos += pos
#                 
#             self.dict_data["command"]=self.tags_string[command]
#             self.dict_data["parameters"]["node_id"] = node_id
#             self.dict_data["parameters"]["auth_app"] = auth_app
#             return self
#          
#         def dump(self):
#             parameters = {"username":self.dict_data["parameters"]["username"].encode(),
#                           "secret":self.dict_data["parameters"]["secret"].encode()}
#             data = struct.pack("IHB",self.dict_data["parameters"]["node_id"],self.dict_data["parameters"]['auth_app'],
#                                self.tags_id[self.dict_data["command"]]
#                                 )
#             for tag in parameters:
#                 data += self.dump_TLV((self.tags_id[tag],len(parameters[tag]),parameters[tag]))  
#             return data            

#         def load(self,bindata):
#             self.dict_data["node_id"],self.dict_data["auth_app"],self.dict_data["command"] = struct.unpack("IHB",bindata[0:7])
#             self.dict_data["parameters"]={}
#             start_pos = 7
#             while start_pos < len(bindata):
#                 pos, result = self.load_TLV(bindata[start_pos:])
#                 self.dict_data["parameters"][result[0]] = result[2]
#                 start_pos += pos
#             return self
#         
#         def dump(self):
# 
#             data = struct.pack("IHB",self.dict_data["node_id"],self.dict_data['auth_app'],
#                                self.dict_data["command"]
#                                 )
#             for tag in self.dict_data["parameters"]:
#                 data += self.dump_TLV((tag,len(self.dict_data["parameters"][tag]),self.dict_data["parameters"][tag]))  
#             return data            
    
    def __init__(self,station,callback=None,apps_dict=None):
        '''
        callback(action,peer_id,action_content,error_code,error_message)
        apps_dict = { 
                        appid:(logincallback(user,node),logoutcallback(user,node),
                        userdb:{user:password,user:password},
                      }
        '''
        super().__init__(station,PP_APPID["Auth"],callback)
        self.apps_dict=apps_dict   
        self.start()
        pass
    
    def login(self,username,secret):
        
        users = self.apps_dict["userdb"]
        #print(users)
        if username in users and secret == users[username]:
            logging.info("%s login success!"%username)
            return True
        logging.warning("%s login failure!"%username)
        return False
        pass
    
    def logout(self,username):
        return True
    
    def send_auth(self,peer_id,auth_app,command,username,secret,node_id):
        app_msg = self.AuthMessage(dictdata={"command":command,
                                          "parameters":{"username":username,
                                                        "secret":secret,
                                                        "auth_app":auth_app,
                                                        "node_id":node_id,}
                                        })
        self.send_msg(peer_id, app_msg,need_ack=True)

    def process(self,pp_msg,addr):

        app_message = self.AuthMessage(bindata=pp_msg.get("app_data"))
        logging.info(app_message.dict_data)
        parameters = app_message.get("parameters")
        username = parameters["username"]
        if app_message.get("command")=="login":
            if self.login(username, parameters["secret"]):

                self.apps_dict[parameters["auth_app"]][0](username,parameters["node_id"])
        if app_message.get("command")=="logout":
            if self.logout(username):
                self.apps_dict[parameters["auth_app"]][1](username,parameters["node_id"])
                        
            
#     def send_auth(self,peer_id,auth_app,command,username,secret,node_id):
#         app_msg = self.AuthMessage(dictdata={"node_id":node_id,"command":command,
#                                           "auth_app":auth_app,
#                                           "parameters":{
#                                                         AUTH_TAG["username"]:username.encode(),
#                                                         AUTH_TAG["secret"]:secret.encode()}
#                                         })
# 
#         self.station.send_msg(peer_id, app_msg,need_ack=True)
    
        
#     def process(self,pp_msg,addr):
# 
#         app_message = self.AuthMessage(bindata=pp_msg.get("app_data"))
#         logging.debug(app_message.dict_data)
#         parameters = app_message.get("parameters")
#         username = parameters[AUTH_TAG["username"]].decode()
#         if app_message.get("command")==AUTH_TAG["login"]:
#             if self.login(username, parameters[AUTH_TAG["secret"]].decode()):
#                 self.apps_dict[app_message.get("auth_app")][0](username,app_message.get("node_id"))
#         if app_message.get("command")==AUTH_TAG["logout"]:
#             if self.logout(username):
#                 self.apps_dict[app_message.get("auth_app")][1](username,app_message.get("node_id"))
      
    
class FileSheller(Sheller):
    '''
    fs = FileShell(station)
    fs.connect(peer_id)
    '''
    def __init__(self,station):
        super().__init__(station)
        self.filer = Filer(station,work_dir="")  

    def get_file(self,remote,local):
        if not self.peer_id:
            return False
        peer_work_dir =  self.shell_client.server_work_dir  
        fullpath_remote = get_fullpath(peer_work_dir,remote)   
        fullpath_local = get_fullpath(os.getcwd(),local)
        self.filer.get_file(peer_id = self.peer_id,remote=fullpath_remote,local=fullpath_local)
        return True
        
    def put_file(self,remote, local):
        if not self.peer_id:
            return False
        peer_work_dir =  self.shell_client.server_work_dir 
        fullpath_remote = get_fullpath(peer_work_dir,remote)   
        fullpath_local = get_fullpath(os.getcwd(),local)        
        self.filer.put_file(peer_id = self.peer_id,remote=fullpath_remote,local=fullpath_local)
        
class FileShellerWithAuth(FileSheller):
    def __init__(self,station):
        super().__init__(station)
        self.auth=Auther(apps_dict={
                          PP_APPID["Shell"]:(self.start_connect,self.start_disconnect),
                          "userdb": station.config["shell_users"]
                          },station=station )

    def start_connect(self,username,peerid):
        if self.peer_id:
            if not self.peer_id ==peerid:
                logging.warning("there have a connection already!")
                info = b"there have a connection already!"
                self.shell_server.info(command_id = 0,info = info,error_code= 1)
                return None
            else:
                info = b"reconnected !"
                self.shell_server.info(command_id = 0,info = info,error_code= 0)
                self.connect(peerid) 
                return (self,peerid)
        self.connect(peerid)        
        time.sleep(2)
        info = b"Welcome use PPLink! "
        self.shell_server.info(command_id = 0,info = info,error_code= 0)
        return (self,peerid)    # as connection handle    
        
    def start_disconnect(self,username,peerid):
        if not self.peer_id:
            logging.warning("no connected %d"%peerid)
            return
        info = b"You are disconnected! "
        self.shell_server.info(command_id = 0,info = info,error_code= 0)
        time.sleep(2)
        self.disconnect()
        logging.info("server disconnected!")
                    
    def show_status(self):
        if self.peer_id:
            print("shell connect  to %d !"%self.peer_id)
        else:
            print("shell no connection!")

    def do_connect(self,peer_id,username,secret):
        self.start_connect(username,peer_id)
        self.auth.send_auth(peer_id,PP_APPID["Shell"],"login",
                            username,secret, self.station.node_id)
        
    
    def do_disconnect(self,peer_id,username):
        self.auth.send_auth(peer_id,PP_APPID["Shell"],"logout",
                            username,"", self.station.node_id)
        self.start_disconnect(username,peer_id)
        return 
            
    def support_commands(self):
        return ["connect","disconnect","shell","!","get","put","stat"]
        
    def run_command(self, command_string):
        cmd = command_string.split(" ")
        if cmd[0] in ["connect","disconnect","shell","!","get","put","stat"]:
            if cmd[0] =="connect" and len(cmd)>=4:
                self.do_connect(int(cmd[1]),cmd[2],cmd[3])
            elif cmd[0] =="disconnect" and self.peer_id:
                self.do_disconnect(self.shell_client.connection[1],"")
            elif cmd[0] in ["shell","!"] and len(cmd)>=2:
                self.shell_command(command_string[len(cmd[0]):])
            elif cmd[0] =="get" and len(cmd)>=3:
                self.get_file(remote = cmd[1],local = cmd[2])
            elif cmd[0] =="put" and len(cmd)>=3:
                self.put_file(local = cmd[1],remote = cmd[2])
            elif cmd[0] =="stat":
                self.show_status()
            else:
                print("error in command")  
            return True
        elif cmd[0] in super().support_commands():
            return super().run_command(command_string)
        elif self.peer_id:
            self.shell_command(command_string)
            return True
        return False
   
        
class PPShellStation(PPStation):
    '''
    . 基于P2P网络的文件共享工具
    . protocol 21
    . protocol srcid  dstid  sequenceid length appdatas
    . appdatas = [fstag len value]
    . fscommand 
    . fsfile
    . fsinfo 
    . 
    
    '''
    def __init__(self,config={}):
        '''
  
        config: 配置文件，共享的文件夹根目录
            #认证模式
            auth_mode =   
                   secret : 共享口令
                   ldap:
                   os:
                   sso:
            secert = password
            
            #共享文件夹根
            share_folder = c:/public   
            
            #pp节点信息
            pp_net = 
            pp_node_id =
             
        '''
        super().__init__(config = config)        
#         self._load_config(self.config)

        self.file_sheller = FileSheller(self)
        self.services.update({"file_shell":self.file_sheller})


    
#     def _load_config(self,config):
#         self.auth_mode = config.get("auth_mode","secret")
#         self.share_folder = config.get("share_folder",".")
#         self.pp_net = config.get("pp_net","public")
#         self.pp_node_id = config.get("node_id",300)
#         self.pp_node_port = config.get("node_port",54320)
#          
#         pass
    
    def start(self):
        super().start()
        self.auth=Auther(apps_dict={
                                  PP_APPID["Shell"]:(self.connect,self.disconnect),
                                  "userdb": self.config["shell_users"]
                                  
                                  },station=self )
        self.set_app_process(PP_APPID["Auth"],self.auth.process)
    
    def quit(self):
        self.set_app_process(PP_APPID["Auth"],None)
        super().quit()
           
    def connect(self,username,peerid):
        if self.file_sheller.peer_id:
            if not self.file_sheller.peer_id ==peerid:
                logging.warning("there have a connection already!")
                info = b"there have a connection already!"
                self.file_sheller.shell_server.info(command_id = 0,info = info,error_code= 1)
                return None
            else:
                info = b"reconnected !"
                self.file_sheller.shell_server.info(command_id = 0,info = info,error_code= 0)
                self.file_sheller.connect(peerid) 
                return (self,peerid)
        self.file_sheller.connect(peerid)        
        time.sleep(2)
        info = b"Welcome use PPLink! "
        self.file_sheller.shell_server.info(command_id = 0,info = info,error_code= 0)
        return (self,peerid)    # as connection handle    
        
    def disconnect(self,username,peerid):
        if not self.file_sheller.peer_id:
            logging.warning("no connected %d"%peerid)
            return
        info = b"You are disconnected! "
        self.file_sheller.shell_server.info(command_id = 0,info = info,error_code= 0)
        time.sleep(2)
        self.file_sheller.disconnect()
        logging.info("server disconnected!")


    def show_status(self):
        if self.file_sheller.peer_id:
            print("connect  to %d !"%self.file_sheller.peer_id)
        else:
            print("no connection!")


    def get_file(self,remote, local):
        self.file_sheller.get_file(remote=remote, local=local)
 
 
    def put_file(self,remote, local):
        self.file_sheller.put_file(local=local, remote=remote)

    def shell(self,command):
        self.file_sheller.shell_command(command)
        
    def do_connect(self,peer_id,username,secret):
        self.auth.send_auth(peer_id,PP_APPID["Shell"],"login",
                            username,secret, self.node_id)
        self.connect(username,peer_id)
        
    
    def do_disconnect(self,peer_id,username):
        self.auth.send_auth(peer_id,PP_APPID["Shell"],"logout",
                            username,"", self.node_id)
        self.disconnect(username,peer_id)
        return 
    
    def run_command(self, command_string):
        cmd = command_string.split(" ")
        if cmd[0] in ["connect","disconnect","shell","!","get","put","stat"]:
            if cmd[0] =="connect" and len(cmd)>=4:
                self.do_connect(int(cmd[1]),cmd[2],cmd[3])
            elif cmd[0] =="disconnect" and self.file_sheller:
                self.do_disconnect(self.file_sheller.shell_client.connection[1],"")
            elif cmd[0] in ["shell","!"] and len(cmd)>=2:
                self.shell(command_string[len(cmd[0]):])
            elif cmd[0] =="get" and len(cmd)>=3:
                self.get_file(remote = cmd[1],local = cmd[2])
            elif cmd[0] =="put" and len(cmd)>=3:
                self.put_file(local = cmd[1],remote = cmd[2])
            elif cmd[0] =="stat":
                super().run_command(command_string)
                self.show_status()
            else:
                print("error in command")        
        elif cmd[0] in super().support_commands():
            super().run_command(command_string)
        elif self.file_sheller.peer_id:
            self.shell(command_string)
        else:
            print("unsupport command!")

def main(config):
    print("PPLink is lanching...")
    station = PPShellStation(config=config)
    station.start()
    try_count = 0
    while not station.status:
        time.sleep(2)
        station.status = station._check_status()
        try_count += 1
        if try_count > 10 or station.quitting:
            break
    print("node_id=%d online=%s" % (station.node_id, station.status))
    
#     if station.status:
#         station.request_path(BroadCastId, 6)
    node_type = config.get("node_type","server")
    is_client = node_type == "client"
    while not is_client and not station.quitting:
        time.sleep(10)

    s= "help"
    while not station.quitting:
        try:
            station.run_command(s)
            if not station.file_sheller.peer_id:
                print("\n%d>"%station.node_id,end="")
        except Exception as exp:
            print(exp.__str__())
        if not station.quitting:
            s=input()

    print("PPlink Quit!")    


if __name__ == '__main__':
    config = {"auth_mode":"secret","secret":"password",
                   "share_folder":".",
                   "net_id":"home","node_id":818300194,
                   "node_port":54320,"DebugLevel":logging.WARNING}
    config = yaml.load(open("fmconfig.yaml"))
    set_debug(config.get("DebugLevel",logging.WARNING),
              config.get("DebugFile",""))

    main(config = config)
    
    pass


    
class Test(unittest.TestCase):


    def setUp(self):
        set_debug(logging.INFO,"")        
        self.stationA = FakeAppNet(100)
        self.stationB = FakeAppNet(200)   
        self.file_shellerA = FileSheller(self.stationA)             
        self.file_shellerB = FileSheller(self.stationB)    
        processes = {100:self.stationA.process_msg,
                     200:self.stationB.process_msg}
        self.stationA.set_process(processes)
        self.stationB.set_process(processes)      

        self.file_shellerA.connect(200)     
        self.file_shellerB.connect(100)    
        pass


    def tearDown(self):
        
        pass
    def auth_callback(self,content,peer_id):
        print("callback",peer_id,content)
        self.auth_id = peer_id
        
    def test_auth(self):
        self.auth_id =  0
        self.authA=Auther(apps_dict={
                                  PP_APPID["Shell"]:(self.auth_callback,self.auth_callback),
                                  "userdb": {"test":"test"}
                                  },station=self.stationA )
        self.authA.start()
        self.authB=Auther(apps_dict={
                                  PP_APPID["Shell"]:(self.auth_callback,self.auth_callback),
                                  "userdb": {"test":"test"}
                                  },station=self.stationB )
        self.authB.start()
        
        self.authA.send_auth(200, PP_APPID["Shell"], "login", "test","test", self.stationA.node_id)
        time.sleep(1)
        self.assertEqual(self.auth_id,100, "test auth  ")        
        pass
    

    def test_dir(self):
   
        self.file_shellerA.shell_client.send_command("dir")
#         time.sleep(1)
        self.assertTrue(self.file_shellerA.shell_client.stage==2,"test dir")
        pass
        
    def test_cd(self):
        self.file_shellerA.shell_client.send_command("cd ..")
#         time.sleep(1)
        self.assertEqual(self.file_shellerB.shell_server.work_dir,
                        os.path.abspath(os.path.join(os.getcwd(), "../")))
 
        pass
        
    def test_get_file(self):
        with open("t.md","wt") as f:
            f.write("test shell\n"*100)
        if os.path.exists("t1.md"):
            os.remove("t1.md")
        self.file_shellerA.get_file("t.md", "t1.md")
#         time.sleep(1)
        self.assertEqual(FileBlock(filename="t.md").get_md5(),
                         FileBlock(filename="t1.md").get_md5(),"get file result")
        pass
         
    def test_put_file(self):
        with open("t.md","wt") as f:
            f.write("test shell\n"*100)
        if os.path.exists("t1.md"):
            os.remove("t1.md")
        self.file_shellerA.put_file(local =  "t.md", remote = "t1.md")
#         time.sleep(1)
        self.assertEqual(FileBlock(filename="t.md").get_md5(),
                         FileBlock(filename="t1.md").get_md5(),"put file result")
        pass

# if __name__ == "__main__":
#     #import sys;sys.argv = ['', 'Test.testName']
#     unittest.main()